Speech for the Disabled

© 2005, Gordon Sweet

author contact:

Gordon@gsweet.fsnet.co.uk

Home

Save As w/o Save

Working with Strings

Stylebits Corner

Eddie's Lessons, v8

Multiple Languages

Speech for Disabled

File Searcher

Newsletter help

Index


Some years ago I used to attend a day center for the mentally disabled, where I attempted to encourage some to make use of a computer. I sometimes surprised the staff with some success in helping those who appeared quite incapable. We were amazed to find the one most skilled at handling a racing car in a program was a young woman, who would often lean over when the car image needed to negotiate a tight curve in the racetrack. Furthermore it was not always the women who had the patience to spend some time at practicing typing.

Two of the unfortunate people, though probably very intelligent, were severely paralysed, and unable to speak. Both made use of a rather crude keyboard strapped to their wheelchairs, able to emit speech sounds. I therefore decided to try to create a Qbasic program to try to help, using an old DOS speech synthesizer, which I have now upgraded using LB thanks to the invaluable help provided by Stefan Pendl.

You will see the user has the choice of activating the set phrases using either the mouse or a joystick. However due to the wide variations in the way different joysticks behave, those attempting to make serious use of this option in an improved version of this program, may need to make certain changes to match a suitable joystick or even tracker-ball. Alternatively it will accept keyboard input and will also produce a printout. The program starts the usual way devised by Stefan to test for the presence in the system for the necessary MS Agents to allow speech to be produced, and provide a warning if there is a problem.

Rather than incorporating some complex method of editing the phrases in PHRASES.DAT, I decided to make use of NOTEPAD, since the file is pure ASCII, and this allows changes to be made independently of the program. As the program caters for the smaller 800 x 600 display in case of poor eyesight, I adopted the practice of providing a background for larger screen displays.

I am sure those with a medical knowledge of dealing with these people could make a number of improvements to this program if they wished. Comments always welcome in the forum.

Editor's Note: For a more indepth discussion and explanation of John White's Text to Speech dll's, see [Demo: Using a Speech Synthesizer] by [Gordon Sweet], [Liberty BASIC Newsletter #117] and [Text to Speech with Liberty BASIC] by Stefan Pendl, [Liberty BASIC Newsletter #118]. These two articles will also provide information about downloading and installing the necessary Microsoft support files and documentation required for speech capability. Since the publications of these two articles, John White has updated the original stm.dll to two separate files: tts.dll (Text to Speech) and ttw.dll (Text to Wavefile). These two files, along with support documentation and John's demo written in Liberty BASIC, can be downloaded at [http://www.yoingco.com/YoingcoDLLs.zip].


' *** NEEDS STM.DLL  IN SAME DIRECTORY ***

' -------------------------------------------------------------------------
' Text to Speech testing for using MS Agent and
' based on tts_tool.c by Martin Goebel
' using stm.dll and ttw.dll by John White
' by Stefan Pendl
' Revision History
' ===============
' Version | Date        | Name         | Reason
' --------|-------------|--------------|-----------------------------------
'  1.0    | 30.Jan.2004 | Stefan Pendl | Initial

    nomainwin
    WindowWidth=DisplayWidth : WindowHeight=DisplayHeight
    open "MS Agent Test" for text_fs as #test
    #test "!trapclose [done]" : testok = 0
'constants:
    TEXT.BUFFER.SIZE = 5000

'variables to preset:
    voiceTotal = 0

'at the very beginning of your app to check if speech is installed:
    open "stm.dll" for dll as #speech
    speechOpen = 1

    rtn = BeginSpeech(TEXT.BUFFER.SIZE)
    if rtn = 1 then
        voiceTotal = CountVoices()
        if voiceTotal > 0 then
            testok = 1
            goto [done]
        else
            notice "Speech API 4 Error";chr$(13);_
                   "Speech API 4 Runtime and/or Voice Engines NOT Installed."
            goto [quittest]
        end if
    else
        notice "Speech Library Error";chr$(13);_
               "MS Agent Core NOT Installed."
        goto [quittest]
    end if

'before ending your app:
[quittest]
    #test "MS Agent support files are downloadable from [http://www.microsoft.com/msagent/downloads/user.asp]"
    #test " "
    #test "MS Agent documentation is found at [http://www.microsoft.com/msagent/downloads/developer.asp]"
    wait
[done]
    if speechOpen = 1 then
        rtn = EndSpeech()
        close #speech
    end if
    close #test
    if testok = 0 then end

'constants:
    TEXT.BUFFER.SIZE = 5000
    NEW.TTS.ALWAYS = 1
    USE.DEFAULT.VOLUME = 0
    USE.DEFAULT.PITCH = 0
    USE.DEFAULT.SPEED = 0
    USE.TEXT = 0
    CLEAR.TEXTSPEECH.BUFFER = 3

'variables to preset:
    voiceTotal = 0
    currentVoice = 0
    voiceNumber = 0
    volume = 0
    pitch = 0
    speed = 0
    intro = 1

[startup]
'at the very beginning of your app to check if speech is installed:
    open "stm.dll" for dll as #speech
    rtn = BeginSpeech(TEXT.BUFFER.SIZE)
    if rtn = 1 then
        voiceTotal = CountVoices()
        if voiceTotal > 0 then
            call EnumVoiceData
            vdPtr = GetVoiceData(currentVoice)
        else
            notice "SAPI4 Error";chr$(13);_
                   "SAPI4 and/or Voice Engines NOT Installed."
            goto [quit]
        end if
    else
        notice "Speech Library Error";chr$(13);_
               "Library Speech NOT Available."
        goto [quit]
    end if

' ******** MAIN MENU ********
    open "Background" for graphics_fs_nsb as #bg
    #bg "trapclose [quit]; down; fill darkgreen; flush"
[menu]
    if win = 9 then close #9
    bx3 = 0 : bx7 = 0 : jm = 0 : win = 0
    ux = 1 : uy = 1
    if DisplayWidth > 1000 then ux = 120 : uy = 90
    UpperLeftX = ux : UpperLeftY = uy
    WindowWidth = 800 : WindowHeight = 580
    joyst$="" : jm = 0 : first = 1

    WindowWidth = 800 : WindowHeight = 600
    button #1, "SPEECH SCREEN", [mquit], UL, 160, 400, 140, 30
    button #1, "ALTER PHRASES", [alter], UL, 320, 400, 140, 30
    button #1, "ABORT/RESTART", [quit], UL, 480, 400, 140, 30
    open "OPTIONS / QUIT" for graphics_nsb as #1
    #1 "trapclose [quit]" : win = 1
    #1 "cls; fill 150 250 150; backcolor 150 250 150"
    #1 "font arial 24 bold; place 300 100" : #1 "\Menu of Options"
    DIM PHRASE$(100)
    if joyst$<>"" then [rescr]
    joyst$ = "OFF"
    Speech$ = "Do you wish to use the Joystick"
    rtn = SpeakString(USE.TEXT,Speech$,CLEAR.TEXTSPEECH.BUFFER,_
    currentVoice,NEW.TTS.ALWAYS,USE.DEFAULT.VOLUME,volume,_
    USE.DEFAULT.PITCH,pitch,USE.DEFAULT.SPEED,speed)
    j$ = "" : confirm Speech$; j$
    if j$ = "yes" then joyst$="ON"
[rescr]
    tx1$ = "\You can change or erase any of the phrases, such as using Windows Notepad. "
    tx2$ = "\The maximum number of possible phrases is 90, each with a limit of 15 letters."
    tx3$ = "\\You can also enter a message using the Keyboard by clicking the KEYS button"
    #1 "cls; fill 150 250 150; backcolor 150 250 150"
    #1 "font arial 24 bold; place 280 100" : #1 "\Menu of Options"
    #1 "font arial 10; place 250 130"
    #1 "\Speech Aid by Gordon Sweet Ver. 2.3 October 2005"
    #1 "\Speech Function by Stefan Pendl   megabit@t-online.at"
    #1 "\using STM.dll by' John White http://www.yoingco.com/main.htm"
    #1 "font arial 13 bold; color darkred; place 80 200"
    #1 tx1$;tx2$;tx3$
    #1 "color black; place 120 340"
    #1 "\THE JOYSTCK IS ";joyst$;".  RESTART THE PROGRAM TO CHANGE THIS."
    #1 "flush" : intro = 0
[mhold]
    wait
[mquit]
    if snd=1 then snd$=""
    if first = 1 then first = 0 : goto [start]
    close #1 : goto [hold]

[start]
    '****** SPEECH SCREEN *******
    close #1 : win = 9
    if j$ = "yes" then gosub [jsinit] : joyst$ = "ON"
    statictext #9.s, snd$, 1, 4, 550, 20
    if joyst$="ON" then  [set]
    button #9, "SPEAK", [speak], UL 560, 4
    button #9, "PRINT", [pout], UL 620, 4
    button #9, "CLEAR", [clear], UL 680, 4
    button #9, "KEYS ", [kbrd], UL 740, 4
[set]
    tx$="Left Click or Button to Select, Right Click for Options and Quit"
    open tx$ for graphics_nsb as #9
    snd=0 : po$=""
    #9 "trapclose [quit]; down"
    if jm = 0 then
        #9 "when leftButtonDown [which]"
        #9 "when rightButtonDown [menu]"
    end if
    snd$="Select phrases below, or click KEYS, then use SPEAK/PRINT/CLEAR"

    OPEN "phrases.dat" FOR INPUT AS #2
    Count = 0: ok=0
    WHILE ok=0
        IF EOF(#2) < 0 THEN ok = 1 : GOTO [none]
        Count = Count + 1
        line input #2, i$ : li=len(i$)
        if li>15 then i$=left$(i$,15)
        PHRASE$(Count)=i$
[none]
    WEND
    CLOSE #2

[hold]
    gosub [show]
    if joyst$="ON" then goto [joyset]
    wait

[clear] snd$="" : if joyst$="OFF" then goto [text]
    #9 "cls; fill 200 200 100"
    gosub [show] : goto [hold]

[top]
    if joyst$="OFF" then [hold]
    if y<6 or y>20 then [hold]
    if x>560 and x<600 then [speak]
    if x>620 and x<660 then [pout]
    if x>680 and x<720 then [clear]
    if x>740 and x<780 then [kbrd]
    goto [hold]

[show]
    #9 "cls; font fixedsys; fill 200 200 100"
    #9 "backcolor darkblue; color white"
    h=30 : C=1 : #9 "place 1 30; down"
    for row=1 to 18
        w=4
        for colm=1 to 5
            #9 "place ";str$(w);" ";str$(h+20)
            phrs=C-1+colm
            t$=PHRASE$(phrs)
            lt=len(t$)
            if lt<16 then t$="\"+" "+t$+space$(16-lt)
            if check=1 then #9 "\";str$(C)
            #9 t$;
            w=w+160
        next colm
        h=h+30 : C=C+5
    next row
    print #9.s, "font arial 12 bold; backcolor white; color black"
    print #9.s, snd$;
    #9 "flush"
    return

[which]
    if snd=0 then snd$="" : snd=1
    if joyst$="OFF" then x=MouseX : y=MouseY
    test$=""
    if x < 4 or x > 780 then [hold]
    if y > 562 then [hold]
    if y < 38 then [top]
    A = 38 : B = 50 : L = 0
    for D = 1 to 18
        if y > A and y < B then L = D
        A = A + 30 : B = B + 30
    next D
    if L = 0 then [hold]
    L = (L - 1) * 5
    A = 4 : B = 138 : K = 0
    for D = 1 to 5
        if x > A and x < B then K = D
        A = A + 160 : B = B + 160
    next D
    if K = 0 then [hold]
    if K = 0 then [hold]
    K = K + L
    test$ = snd$ + PHRASE$(K) + "  "
    if len(test$) > 70 then
        notice "Tool long! Click SPEAK\PRINT\CLEAR"
        goto [hold]
    end if
    snd$ = test$ 
[text]
    print #9.s, space$(136) : print #9.s, snd$;
    goto [hold]

[kbrd]
    if bx7 = 1 then close #7
    snd = 1 : i$=""
    if left$(snd$,4) = "****" then snd$ = ""
    WindowWidth = 500 : WindowHeight = 150
    UpperLeftX = 180+ux : UpperLeftY = 100+uy
    BackgroundColor$ = "green" :  ForegroundColor$ = "black"
    TextboxColor$ = "white"
    tx$="Maximum characters 70 including text already shown"
    statictext #7, tx$, 6, 10, 790, 20
    textbox #7.tbox1, 10, 30, 470, 25
    button #7.default, "Accept", [pass], UL, 100, 70, 100, 25
    button #7, "Cancel", [bfin], UL, 300, 70, 100, 25
    open "Enter message" for dialog as #7
    #7 "trapclose [again]" : #7 "font fixedsys"
[again]
    print #7.tbox1, snd$
    bx=1 : playwave "ping.wav"
    mainH=hWnd(#7) : gosub [boxhold]
    wait
[pass]
    print #7.tbox1, "!contents? A$"
    if len(i$)+len(A$) > 70 then
        notice "Exceed Limit!"
        goto [again]
    end if
    snd$=A$+" "
[bfin] bx7=0 : close #7 : goto [text]

[pout]
    if left$(snd$,3)="***" then [hold]
    lprint snd$;" ";
    confirm "Printout now, otherwise later"; po$
    if po$="yes" then dump
    goto [hold]

[alter]
    run "notepad.exe phrases.dat", restore
    goto [rescr]

[jsinit]
    open "winmm.dll" for dll as #mm
    Struct JOYINFO, _
        x As long , _   'x position
        y As long , _   'y position
        z As long , _   'z position
        buttons As long 'buttons pushed
    JOYSTICK1 = 0 : JOYSTICK2 = 1
    timer 50, [checkJoy]
    x=400 : y=300 : jm = 1
    return

[jloop]
    x1=x : y1=y
    #9 "backcolor red";
    wait


[checkJoy]
    READJOYSTICK 1
    X=Joy1x : Y=Joy1y : b1=Joy1button1 : b2=Joy1button2

    if X<30000 and x>2 then x=x-3
    if X>40000 and x<784 then x=x+3
    if Y<20000 and y>4 then y=y-3
    if Y>40000 and y<566 then y=y+3
    #9 "place ";str$(x1);" ";str$(y1);
    #9 "rule XOR; circlefilled 3";
    #9 "place ";str$(x);" ";str$(y);
    #9 "circlefilled 3";
    if b1 then gosub [delay] : goto [which]
    if b2 then gosub [delay] : goto [speak]
    goto [jloop]

[delay]
    scan:calldll #kernel32,"Sleep",300 as ulong,r as void
    return

[joyset]
    #9 "when leftButtonDown [hold]"
    #9 "color black; backcolor lightgray; place 560 20" :  #9 "\SPEAK"
    #9 "place 620 20" : #9 "\PRINT"
    #9 "place 680 20" : #9 "\CLEAR"
    #9 "place 740 20" : #9 "\KEYS "
    goto [jloop]

[quit]
    rtn = EndSpeech()
    close #speech
    intro = 0
    if po$<>"" then dump
    if bx3 = 1 then close #3
    if bx7 = 1 then close #7
    if win = 9 then close #9
    if win = 1 then close #1
    if jm = 1 then close #mm
    close #bg
    end

[boxhold]
    open "user32" for dll as #user ' prevents losing box
        toTop=(-1 or 0)
        flags=_SWP_NOMOVE or _SWP_NOSIZE
    calldll #user,"SetWindowPos",mainH as ushort,toTop as short,_
    0 as short,0 as short,0 as short,0 as short,flags as ushort,_
    result as void
    close #user : return

[speak]
    Speech$ = lower$(snd$)
    rtn = SpeakString(USE.TEXT,Speech$,CLEAR.TEXTSPEECH.BUFFER,_
    currentVoice,NEW.TTS.ALWAYS,USE.DEFAULT.VOLUME,volume,_
    USE.DEFAULT.PITCH,pitch,USE.DEFAULT.SPEED,speed)
    goto [hold]

'functions:
function BeginSpeech(BufferSize)
    calldll #speech, "Begin_Speech", BufferSize  as long, BeginSpeech as long
end function
' memory buffer size in bytes; >= 5000     1 ... success; 0 ... error

function GetVoiceData(VoiceIndex)
    calldll #speech, "Get_VoiceData",_
     VoiceIndex as long, GetVoiceData as long
end function

function SpeakString(SourceFlag,Text$,ClearFlag, VoiceIndex,TTSFlag, VolumeFlag,Volume,PitchFlag,Pitch,SpeedFlag,Speed)
    TextBuffer$ = Text$ + chr$(0)

    calldll #speech, "Speak_String",_
        SourceFlag  as long,_
        TextBuffer$ as ptr,_
        ClearFlag   as long,_
        VoiceIndex  as long,_
        TTSFlag     as long,_
        VolumeFlag  as long,_
        Volume      as long,_
        PitchFlag   as long,_
        Pitch       as long,_
        SpeedFlag   as long,_
        Speed       as long,_
        SpeakString as long
end function

function EndSpeech()
    calldll #speech, "End_Speech", EndSpeech as long
end function

'subroutines:
sub EnumVoiceData
    calldll #speech, "Enum_VoiceData", result as long
end sub

function CountVoices()
    calldll #speech, "Count_Voices", CountVoices as long
end function
' 0 if no voices are installed, else the number of installed voices


The files speechsynthesizer.bas, phrases.dat and stm.dll are included in the zipped archive of this newsletter.